package pl.edu.pw.MidEmu.UI.Components;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.Hashtable;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

import pl.edu.pw.MidEmu.Base.Components.BaseComponent;
import pl.edu.pw.MidEmu.Common.Consts;
import pl.edu.pw.MidEmu.Common.Models.ComponentTypeModel;
import pl.edu.pw.MidEmu.Common.Models.EthernetInterfaceModel;
import pl.edu.pw.MidEmu.Logic.ProcessHandler;
import pl.edu.pw.MidEmu.Sensing.Sensors.ASensor;
import pl.edu.pw.MidEmu.UI.Controllers.MidasComponentController;
import pl.edu.pw.MidEmu.UI.Views.MidasComponentView;

/**
 * Component representing MidEMu virtual node
 * @author Mateusz Starodab
 *
 */
public class MidasComponent extends BaseComponent<JPanel, MidasComponentController> implements MidasComponentView
{
	private static final long serialVersionUID = -3769768175782698313L;

	private ComponentTypeModel componentType;
	private JLabel imgImage;

	private JLabel lblCaption;
	private JMenuItem miAddSensor;
	private JMenuItem miLoadApp;
	private JMenuItem miRemove;
	private JMenuItem miShowApps;
	private JMenuItem miShowOutput;
	private JMenuItem miShowSensors;
	private JMenuItem miStartCBRD;
	private JMenuItem miStartMidas;
	private JMenuItem miStopCBRD;
	private JMenuItem miStopMidas;

	/**
	 * @uml.property  name="name"
	 */
	private String name;

	private Rectangle orginalDimmentions;

	private MainFrame parent;

	private JPopupMenu pmContext;

	private Repainter repainter;

	public MidasComponent(MainFrame parent, ComponentTypeModel model, int sameCount)
	{
		this.parent = parent;

		MidasComponentController controller = new MidasComponentController();
		controller.setView(this);
		setController(controller);
		JPanel panel = new JPanel();
		setComponent(panel);

		componentType = model;

		FontRenderContext fontRednderContex = ((Graphics2D) parent.getComponent().getGraphics()).getFontRenderContext();
		Font font = getComponent().getFont();
		name = model.getComponentName() + " " + sameCount;
		Rectangle2D rect = font.getStringBounds(name, fontRednderContex);
		int textHeight = (int) Math.round(rect.getHeight());
		int textWidth = (int) Math.round(rect.getWidth());
		int textLeftPos = (int) ((Math.max(model.getIconWidth(), rect.getWidth()) / 2) - (rect.getWidth() / 2));
		rect.setFrame(new Point(0, 0), new Dimension(Math.max(model.getIconWidth(), (int) Math.round(rect.getWidth())) + 10, (int) Math.round(rect.getHeight()) + model.getIconHeight() + 1));

		getComponent().setLayout(null);
		getComponent().setBounds(rect.getBounds());
		getComponent().setOpaque(false);
		getComponent().addMouseListener(getController());
		getComponent().addMouseMotionListener(getController());

		imgImage = new JLabel(model.getIcon());
		imgImage.setBounds((int) ((rect.getWidth() / 2) - (model.getIconWidth() / (double) 2)), 0, model.getIconWidth(), model.getIconHeight());
		getComponent().add(imgImage);

		lblCaption = new JLabel(name);
		lblCaption.setBounds(textLeftPos, model.getIconHeight(), textWidth + 10, textHeight);
		getComponent().add(lblCaption);

		pmContext = new JPopupMenu();
		miLoadApp = new JMenuItem(Consts.MidasComponent_LoadAppMenu);
		miLoadApp.setName(Consts.MidasComponent_LoadAppMenu);
		miLoadApp.setEnabled(false);
		miLoadApp.addActionListener(getController());
		miShowApps = new JMenuItem(Consts.MidasComponent_ShowApps);
		miShowApps.setEnabled(false);
		miShowApps.addActionListener(getController());
		miShowApps.setName(Consts.MidasComponent_ShowApps);
		miShowOutput = new JMenuItem(Consts.MidasComponent_ShowOutput);
		miShowOutput.setEnabled(false);
		miShowOutput.addActionListener(getController());
		miShowOutput.setName(Consts.MidasComponent_ShowOutput);
		miAddSensor = new JMenuItem(Consts.MidasComponent_AddSensor);
		miAddSensor.addActionListener(getController());
		miAddSensor.setName(Consts.MidasComponent_AddSensor);
		miAddSensor.setEnabled(false);
		miShowSensors = new JMenuItem(Consts.MidasComponent_ShowSensors);
		miShowSensors.addActionListener(getController());
		miShowSensors.setName(Consts.MidasComponent_ShowSensors);
		miShowSensors.setEnabled(false);
		miRemove = new JMenuItem(Consts.MidasComponent_RemoveComponent);
		miRemove.addActionListener(getController());
		miRemove.setName(Consts.MidasComponent_RemoveComponent);
		miStartMidas = new JMenuItem(Consts.MidasComponent_StartMidas);
		miStartMidas.addActionListener(getController());
		miStartMidas.setName(Consts.MidasComponent_StartMidas);
		miStopMidas = new JMenuItem(Consts.MidasComponent_StopMidas);
		miStopMidas.addActionListener(getController());
		miStopMidas.setName(Consts.MidasComponent_StopMidas);
		miStopMidas.setVisible(false);
		miStartCBRD = new JMenuItem(Consts.MidasComponent_StartCBRD);
		miStartCBRD.addActionListener(getController());
		miStartCBRD.setName(Consts.MidasComponent_StartCBRD);
		miStartCBRD.setEnabled(false);
		miStopCBRD = new JMenuItem(Consts.MidasComponent_StopCBRD);
		miStopCBRD.addActionListener(getController());
		miStopCBRD.setName(Consts.MidasComponent_StopCBRD);
		miStopCBRD.setVisible(false);

		pmContext.add(miStartMidas);
		pmContext.add(miStopMidas);
		pmContext.add(miStartCBRD);
		pmContext.add(miStopCBRD);
		pmContext.addSeparator();
		pmContext.add(miLoadApp);
		pmContext.add(miShowApps);
		pmContext.addSeparator();
		pmContext.add(miAddSensor);
		pmContext.add(miShowSensors);
		pmContext.addSeparator();
		pmContext.add(miShowOutput);

		pmContext.addSeparator();
		pmContext.add(miRemove);

		orginalDimmentions = getComponent().getBounds();

		repaint();
	}

	public void addRunningApplication(ProcessHandler processHandler)
	{
		getController().addApplication(processHandler);
	}

	@Override
	public void changeMenusForCBRD(boolean b)
	{
		miStopCBRD.setVisible(b);
		miStartCBRD.setVisible(!b);
		miStartCBRD.setEnabled(getController().isMidasRunning());
	}

	@Override
	public void changeMenusForMidas(boolean b)
	{
		miStopMidas.setVisible(b);
		miStartMidas.setVisible(!b);
		miShowOutput.setEnabled(b);
		miShowApps.setEnabled(b);
		miLoadApp.setEnabled(b);
		miAddSensor.setEnabled(b);
		miShowSensors.setEnabled(b);
		miStartCBRD.setEnabled(b);
		if (b)
		{
			miStartCBRD.setVisible(b);
			miStopCBRD.setVisible(!b);
		}
		if (!b)
		{
			miStartCBRD.setVisible(!b);
			miStopCBRD.setVisible(b);
		}
	}

	@Override
	public void dispose()
	{
		parent.remove(this);
	}

	@Override
	public void drawSensors()
	{
		for (int i = 0; i < getComponent().getComponents().length; i++)
		{
			if (getComponent().getComponents()[i] != lblCaption && getComponent().getComponents()[i] != imgImage)
			{
				getComponent().remove(i);
				i--;
			}
		}

		getComponent().setBounds(new Rectangle((int) Math.round(getComponent().getX() - (orginalDimmentions.getWidth() / 2) + (getComponent().getWidth() / 2)), getComponent().getY(), (int) Math.round(orginalDimmentions.getWidth()), (int) Math.round(orginalDimmentions.getHeight())));

		for (ASensor sensor : getController().getSensors())
		{
			FontRenderContext fontRednderContex = ((Graphics2D) parent.getComponent().getGraphics()).getFontRenderContext();
			Font font = getComponent().getFont();
			String sensorValue = sensor.toString() + " :: " + sensor.getValue().getValueAsText();
			Rectangle2D textRect = font.getStringBounds(sensorValue, fontRednderContex);
			textRect.add(textRect.getWidth() + 14, 0);

			Rectangle componentRect = getComponent().getBounds();

			double newWidth = Math.max(textRect.getWidth(), componentRect.getWidth());

			double newHeight = componentRect.getHeight() + textRect.getHeight();

			getComponent().setBounds(new Rectangle((int) Math.round(componentRect.x - (newWidth / 2) + (componentRect.getWidth() / 2)), componentRect.y, (int) Math.round(newWidth), (int) Math.round(newHeight)));
			Rectangle newComponentRect = getComponent().getBounds();

			JLabel lblSensor = new JLabel(sensorValue);
			lblSensor.setForeground(Color.BLUE);
			lblSensor.setBounds(new Rectangle((int) Math.round((newComponentRect.getWidth() / 2) - (textRect.getWidth() / 2)), componentRect.height, (int) Math.round(textRect.getWidth()), (int) Math.round(textRect.getHeight())));

			getComponent().add(lblSensor);
		}

		Rectangle newComponentRect = getComponent().getBounds();
		for (Component component : getComponent().getComponents())
		{
			Rectangle innerComponentRect = component.getBounds();
			component.setBounds((int) Math.round((newComponentRect.getWidth() / 2) - (innerComponentRect.getWidth() / 2)), innerComponentRect.y, innerComponentRect.width, innerComponentRect.height);
		}
	}

	@Override
	public void enableMenuItems()
	{
		miShowApps.setEnabled(true);
		miShowOutput.setEnabled(true);
	}

	@Override
	public void enableSensorMenuItems()
	{
		miShowSensors.setEnabled(true);
	}

	public void enableStartMidasMenu(boolean b)
	{
		miStartMidas.setEnabled(b);
	}

	@Override
	public void freeIfaceByName(String interfaceName)
	{
		parent.getController().freeIfaceByName(interfaceName);
	}

	@Override
	public List<ProcessHandler> getApplications()
	{
		return getController().getApplications();
	}

	public String getCentralNodeIP()
	{
		return parent.getController().getCentralNodeIP();
	}

	public MidasComponent getCentralNodeModel()
	{
		return parent.getController().getCentralNodeModel();
	}

	/**
	 * @return
	 * @uml.property  name="componentType"
	 */
	@Override
	public ComponentTypeModel getComponentType()
	{
		return componentType;
	}

	public List<EthernetInterfaceModel> getFreeInterfaces()
	{
		return parent.getController().getFreeInterfaces();
	}

	@Override
	public Point getLocationOnScreen()
	{
		return getComponent().getLocationOnScreen();
	}

	/**
	 * @return
	 * @uml.property  name="name"
	 */
	@Override
	public String getName()
	{
		return name;
	}

	@Override
	public Point getParentLocationOnScreen()
	{
		return getComponent().getParent().getLocationOnScreen();
	}

	public void init()
	{
		repainter = new Repainter(this.getClass().getName(), "repaint", this);
		repainter.start();
	}

	public boolean isCentralNodeRunning()
	{
		return parent.getController().checkCentralNodeRunning();
	}

	@Override
	public void repaint()
	{
		drawSensors();
		getComponent().repaint();
	}

	@Override
	public void setBounds(int i, int j, int width, int height)
	{
		getComponent().setBounds(i, j, width, height);
		orginalDimmentions.x = i;
		orginalDimmentions.y = j;
	}

	@Override
	public void showAddSensorWindow(Point point)
	{
		AddSensorFrame frame = new AddSensorFrame(this);
		frame.getComponent().setLocation(point);
		frame.getComponent().setVisible(true);
	}

	@Override
	public void showClasLoadingFrame(Point p)
	{
		ApplicationsListFrame list = new ApplicationsListFrame(this);
		list.getComponent().setLocation(p);
		list.getComponent().setVisible(true);
	}

	@Override
	public void showContextMenu(Component component, int x, int y)
	{
		changeMenusForMidas(getController().isMidasRunning());
		changeMenusForCBRD(getController().isCBRDRunning());

		pmContext.show(component, x, y);
	}

	@Override
	public void showOutputWindow(Point p)
	{
		ApplicationOutputsFrame frame = new ApplicationOutputsFrame(this);
		frame.getComponent().setLocation(p);
		frame.getComponent().setVisible(true);
	}

	@Override
	public void showRunningApplications(Point p)
	{
		RunningApplicationsListFrame frame = new RunningApplicationsListFrame(this);
		frame.getComponent().setLocation(p);
		frame.getComponent().setVisible(true);
	}

	@Override
	public void showShowSensorWindow(Point point)
	{
		ShowSensorsFrame frame = new ShowSensorsFrame(this);
		frame.init();
		frame.getComponent().setLocation(point);
		frame.getComponent().setVisible(true);
	}

	@Override
	public void showStartCBRDFrame(Point point)
	{
		CBRDLoaderFrame frame = new CBRDLoaderFrame(this);
		frame.getComponent().setLocation(point);
		frame.getComponent().setVisible(true);
	}

	@Override
	public void showStartMidasFrame(Point point)
	{
		MidasLoaderFrame frame = new MidasLoaderFrame(this);
		if (frame.canRun())
		{
			frame.getComponent().setLocation(point);
			frame.getComponent().setVisible(true);
		}
		else
		{
			frame.dispose();
		}
	}

	public void terminate()
	{
		getController().terminate();
	}

	public Hashtable<String, String> getNodesIDsAndIPs()
	{
		return parent.getNodesIDsAndIPs();
	}

	public Hashtable<String, String> getNodesInterfacesAndIPS()
	{
		return parent.getNodesInterfacesAndIPS();
	}
}
